package cn.icanci.loopstack.amc.cloud.biz.repository;

import cn.icanci.loopstack.amc.cloud.biz.exception.MockCallScriptException;
import cn.icanci.loopstack.amc.cloud.dal.mongodb.daointerface.AppMockCallDAO;
import cn.icanci.loopstack.amc.cloud.dal.mongodb.dataobject.AppMockCallDO;
import cn.icanci.loopstack.amc.spi.mock.MockCallStandardHandler;
import cn.icanci.loopstack.script.LsiScriptEngineFactory;
import cn.icanci.loopstack.script.compile.GroovyClassLoaderHolder;
import cn.icanci.loopstack.script.enums.ScriptTypeEnum;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.annotation.Resource;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * @author icanci
 * @since 1.0 Created in 2023/01/16 15:36
 */
@Service
public class AmcMockCallRepository {

    private final Logger                                logger                   = LoggerFactory.getLogger(AmcMockCallRepository.class);
    /**
     * 判断是否是第一次加载 变量标识
     */
    private static final AtomicBoolean                  ATOMIC_BOOLEAN_REFRESHED = new AtomicBoolean(false);

    /**
     * 执行缓存
     * 
     * key: uuid
     * value: 配置数据
     */
    private static final Map<String, AmcMockCallHolder> MOCK_CALL_HOLDER_MAP     = new ConcurrentHashMap<>();

    @Resource
    private AppMockCallDAO                              appMockCallDAO;

    /**
     * 刷新本地执行仓储
     * 
     * @param mockCallUuid mockCallUuid
     */
    public void refresh(String mockCallUuid) {
        // 1.判断是否是第一次加载
        if (!ATOMIC_BOOLEAN_REFRESHED.get()) {
            // 1.1 如果是第一次加载，则加载所有的数据，然后再加载mockCallUuid
            refreshAll();
            // 1.1.1 加载结束之后设置标识
            ATOMIC_BOOLEAN_REFRESHED.compareAndSet(false, true);
        }
        // 1.2 如果不是第一次加载，则加载mockCallUuid
        refreshForUuid(mockCallUuid);
    }

    /**
     * 刷新缓存所有数据
     */
    private void refreshAll() {
        List<AppMockCallDO> appMockCallList = appMockCallDAO.queryAll();
        if (CollectionUtils.isEmpty(appMockCallList)) {
            logger.warn("[AmcMockCallRepository][refreshAll] appMockCallList is Empty!");
            return;
        }
        for (AppMockCallDO appMockCall : appMockCallList) {
            if (appMockCall.getIsDelete() == 1) {
                continue;
            }
            refresh(appMockCall);
        }
    }

    /**
     * 刷新单独组件
     * 
     * @param appMockCall appMockCall
     */
    private void refresh(AppMockCallDO appMockCall) {
        try {
            String appMockUuid = appMockCall.getUuid();
            ScriptTypeEnum scriptType = ScriptTypeEnum.valueOf(appMockCall.getScriptType());
            String script = appMockCall.getScript();

            AmcMockCallHolder holder = new AmcMockCallHolder();
            holder.setMockCallUuid(appMockUuid);
            holder.setMockCallName(appMockCall.getMockName());
            holder.setScriptType(scriptType);
            holder.setScriptContent(script);

            ScriptEngine scriptEngine = LsiScriptEngineFactory.getScriptEngine(scriptType);
            holder.setScriptEngine(scriptEngine);
            if (scriptEngine instanceof Compilable) {
                Compilable compilable = (Compilable) scriptEngine;
                holder.setCompilable(compilable);
                CompiledScript compile = compilable.compile(script);
                holder.setCompiledScript(compile);
            }
            Class<?> compileClass = GroovyClassLoaderHolder.compile(script);
            Object instance = compileClass.newInstance();
            if (instance instanceof MockCallStandardHandler) {
                holder.setHandlerClass((Class<MockCallStandardHandler>) compileClass);
                // TODO 后置处理器 注入一些工具bean之类，此版本暂时没有
                holder.setInstance((MockCallStandardHandler) instance);
            }
            MOCK_CALL_HOLDER_MAP.put(appMockUuid, holder);
        } catch (Exception ex) {
            logger.error("[AmcMockCallRepository][refresh] Compile Script error:{}", ex.getLocalizedMessage());
            throw new MockCallScriptException(ex);
        }
    }

    /**
     * 只刷新指定的uuid
     * 
     * @param mockCallUuid mockCallUuid
     */
    private void refreshForUuid(String mockCallUuid) {
        AppMockCallDO appMockCall = appMockCallDAO.queryByUuid(mockCallUuid);
        if (appMockCall == null) {
            logger.warn("[AmcMockCallRepository][refreshForUuid] appMockCall is Null!");
            return;
        }
        if (appMockCall.getIsDelete() == 1) {
            logger.warn("[AmcMockCallRepository][refreshForUuid] appMockCall is Offline!");
            return;
        }
        refresh(appMockCall);
    }

    /**
     * 获取仓储数据
     * 
     * @param mockCallUuid mockCallUuid
     * @return 返回仓储数据
     */
    public AmcMockCallHolder getAmcMockCallHolder(String mockCallUuid) {
        return MOCK_CALL_HOLDER_MAP.get(mockCallUuid);
    }
}
